# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         import_api_task.py
# Description:  导入接口数据
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2019/12/12 11:44
# -------------------------------------------------------------------------------
import json
import logging
from json import JSONDecodeError

from apps.HttpAutoTestService.core.builtin.functions import get_timestamp
from apps.HttpAutoTestService.models import Api, Module, Testcase

__all__ = ['import_api_from_postman', 'import_api_from_har', 'import_api_from_yaml', 'import_api_from_yapi']

logger = logging.getLogger(__name__)


def import_api_from_postman(user=None, project=None, contents=None, create_testcase=False):
    """
    通过postman导入接口数据
    Args:
        user: 操作用户
        project: 所属项目
        contents: 文件内容
        create_testcase: 创建测试用例

    Returns:

    """

    # 定义解析接口数据的方法
    def _parse_api(content, module, create_testcase):
        if isinstance(content, list):
            for item in content:
                _parse_api(content=item, module=module, create_testcase=create_testcase)
        elif isinstance(content, dict):
            if 'item' in content.keys():
                module_name = content.get('name')
                module = None
                if module_name:
                    module = Module.objects.filter(name=module_name, project_id=project.id, is_deleted=False).first()
                    if not module:
                        # module在系统中不存在
                        if module_name in module_name_list:
                            # Module已经存在于module_list列表中
                            for module_temp in module_list:
                                if module_name == module_temp.name:
                                    module = module_temp
                                    break
                        else:
                            # Module不存在于module_list列表中，新建Module，并将其存放到module_list列表中
                            module = Module(**{
                                'name': module_name,
                                'project': project,
                                'creator': user,
                                'modifier': user
                            })
                            module_name_list.append(module_name)
                            module_list.append(module)
                _parse_api(content=content.get('item'), module=module, create_testcase=create_testcase)
            elif 'request' in content.keys():
                # api名称
                name = content.get('name')
                # api请求参数类型
                request_data_type = 'Json'
                # api请求参数
                request_data = {}
                request = content.get('request')
                if request:
                    # api请求方法
                    method = request.get('method', 'GET').upper()
                    # api请求头
                    headers = request.get('header')
                    headers = {item.get('key'): item.get('value') for item in headers} if headers else {}
                    # 请求认证
                    auth = request.get('auth')
                    if auth:
                        auth_type = auth.get('type')
                        if auth.get(auth_type):
                            auth_value = {item.get('key'): item.get('value') for item in auth.get(auth_type) if
                                          (item and item.get('key'))}
                            headers.update(auth_value)

                    # api请求URL
                    url = ''
                    # api查询参数
                    request_params = ''
                    # postman请求url（包含请求路径和查询参数）
                    request_url = request.get('url')
                    if request_url:
                        path = request_url.get('path')
                        query = request_url.get('query')
                        if path:
                            url = r'/'.join(path)
                        if query:
                            request_params = '&'.join(
                                [f"{item.get('key')}={item.get('value', '')}" for item in query if item])
                    # postman请求消息体
                    request_body = request.get('body')
                    if request_body:
                        # api接口请求参数类型
                        request_mode = request_body.get('mode')
                        # api接口请求参数
                        request_data = request_body.get(request_mode)
                        if request_data and 'raw' == request_mode:
                            request_data_type = 'Json'
                            try:
                                request_data = json.loads(request_data)
                            except JSONDecodeError:
                                request_data = {}
                        elif request_data and request_mode in ('formdata', "urlencoded"):
                            request_data_type = 'Form Data'
                            if isinstance(request_data, list):
                                request_data = {item.get('key'): '' for item in request_data}

                    # 已存在的api
                    api = Api.objects.filter(project_id=project.id, url=url, method=method, is_deleted=False).first()
                    if not api:
                        # 接口未存在于系统中，需要新增
                        api = Api(name=name, url=url, request_params=request_params,
                                  method=method, headers=headers, request_data_type=request_data_type,
                                  request_data=request_data, project=project, module=module, creator=user,
                                  modifier=user, parameter_desc=parameter_desc)
                        api_list.append(api)
                    else:
                        logger.info('通过postman文件导入的接口已存在，接口：{}'.format(url + '\t' + method))
                    if create_testcase:
                        testcase_name = name
                        if Testcase.objects.filter(name=testcase_name):
                            testcase_name = f'{testcase_name}-{str(get_timestamp(16))[10:]}'
                            # testcase_name = '{testcase_name}-{timestamp}'.format(testcase_name=testcase_name,
                            #                                             timestamp=str(get_timestamp(16))[10:])
                        testcase = Testcase(name=testcase_name, url=url, request_params=request_params, headers=headers,
                                            request_data_type=request_data_type, request_data=request_data,
                                            expect_result=expect_result, api=api, testcase_folder=testcase_folder,
                                            creator=user, modifier=user)
                        testcase_list.append(testcase)

    # 将要新增的模块列表
    module_list = []
    # 存放创建了Module的名称
    module_name_list = []
    # 将要新增的接口
    api_list = []
    # 将要新增的用例
    testcase_list = []
    # 项目默认用例夹
    testcase_folder = project.testcase_folder.filter(is_system=True).first()
    parameter_desc = '## 接口参数定义\n### 请求头参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|\n### 路径参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|\n### 查询参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|\n### Body参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|\n### 响应结果参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|'
    # 测试用例默认期望结果
    expect_result = [{
        'output': [],
        'validate': [{'check': 'status_code', 'expect': 200, 'comparator': 'eq'}],
    }]
    for content in contents:
        content = json.loads(content)
        _parse_api(content=content, module=None, create_testcase=create_testcase)
    Module.objects.bulk_create(module_list)
    Api.objects.bulk_create(api_list)
    msg = f'通过postman文件成功导入{len(module_list)}个模块，{len(api_list)}个接口'
    if create_testcase:
        Testcase.objects.bulk_create(testcase_list)
        msg = f'通过postman文件成功导入{len(module_list)}个模块，{len(api_list)}个接口，{len(testcase_list)}个用例'
    logger.info(msg)
    return msg


def import_api_from_har(user=None, project=None, contents=None, create_testcase=False):
    """
    通过har文件导入接口数据
    Args:
        user: 操作用户
        project: 所属项目
        contents: 文件内容

    Returns:

    """
    # 已经存在的接口
    exist_api_list = []
    total = 0
    pass_total = 0
    fail_total = 0
    fail_api_list = []

    def _parse_data_type(values):
        if isinstance(values, list):
            record = {}
            for val in values:
                if 'name' in val and 'value' in val:
                    name = val.pop("name")
                    value = val.pop("value")
                    value = _parse_data_type(value)
                    record[name] = value
                record.update(val)
            return record
        elif isinstance(values, dict):
            result = {key: _parse_data_type(val) for key, val in values.items()}
            return result
        return values

    for content in contents:
        if content.startswith(u'\ufeff'):
            content = json.loads(content.replace(u'\ufeff', ''))

        entries = content.get("log", {}).get("entries", [])

        for entry in entries:
            request = {key: _parse_data_type(value) for key, value in entry.get("request", {}).items() if
                       key in ['postData', 'headers', 'url', 'method']}

            url = request.get('url', '').split('?')[0].split('/', 3)[-1]
            method = request.get('method', 'GET').upper()
            if not Api.objects.filter(project_id=project.id, url=url, method=method, is_deleted=False):
                api = Api()
                # api创建人
                api.creator = user
                # api修改人
                api.modifier = user
                # api所属项目
                api.project = project

                url = request.get('url', '')
                api.url = url.split('?')[0].split('/', 3)[-1]
                api.request_params = url.split('?')[-1] if url.find('?') != -1 else ''
                api.name = api.url[:128]

                api.method = request.get('method', 'GET').upper()

                post_data = request.get('postData')
                mime_type = post_data.get('mimeType')
                api.headers = json.dumps({'Content-Type': mime_type}, ensure_ascii=False) if mime_type else '{}'
                api.request_data_type = 'Form Data' if mime_type in ['multipart/form-data',
                                                                     'application/x-www-form-urlencoded'] else 'Json'
                if 'params' in post_data.keys():
                    api.request_data = json.dumps(post_data.get('params'), ensure_ascii=False) if post_data.get(
                        'params') else '{}'
                elif 'text' in post_data.keys():
                    api.request_data = post_data.get('text') if post_data.get('text') else '{}'
                api.parameter_desc = """
                    ## 接口参数定义
                    ### 请求头参数定义
                    |参数名称|类型|是否必须|默认值|描述|
                    |-|-|-|-|-|
                    ### 路径参数定义
                    |参数名称|类型|是否必须|默认值|描述|
                    |-|-|-|-|-|
                    ### 查询参数定义
                    |参数名称|类型|是否必须|默认值|描述|
                    |-|-|-|-|-|
                    ### Body参数定义
                    |参数名称|类型|是否必须|默认值|描述|
                    |-|-|-|-|-|
                    ### 响应结果参数定义
                    |参数名称|类型|是否必须|默认值|描述|
                    -|-|-|-|-|
                """
                if api.method + api.url not in exist_api_list:
                    total += 1
                    exist_api_list.append(api.method + api.url)
                    try:
                        api.save()
                        pass_total += 1
                    except Exception as e:
                        fail_total += 1
                        fail_api_list.append(api.url + '\t' + api.method)
                        logger.info(
                            '通过har文件导入接口失败，接口：{api}，原因：{exception}'.format(api=api.url + '\t' + api.method,
                                                                           exception=e))
            else:
                logger.info('通过har文件导入的接口已存在，接口：{}'.format(url + '\t' + method))

    msg = '通过har文件导入{total}个接口，成功{pass_total}个，失败{fail_total}个，失败接口：{fail_api_list}'.format(
        total=total, pass_total=pass_total, fail_total=fail_total, fail_api_list=str(fail_api_list))
    logger.info(msg)
    return msg


def import_api_from_yaml(user=None, project=None, contents=None, create_testcase=False):
    """
    通过yaml导入接口数据
    Args:
        user: 操作用户
        project: 所属项目
        contents: 文件内容

    Returns:

    """
    for item in contents:
        print(item)
    return


def import_api_from_yapi(user=None, project=None, contents=None, create_testcase=False):
    """
    通过yapi json导入接口数据
    Args:
        user: 操作用户
        project: 所属项目
        contents: 文件内容

    Returns:

    """
    # 将要新增的模块列表
    module_list = []
    # 存放创建了Module的名称
    module_name_list = []
    # 将要新增的接口
    api_list = []

    def _parse_api(content):
        if isinstance(content, list):
            for item in content:
                # 获取模块名
                module_name = item.get('name')
                # 获取接口列表
                apis = item.get('list')
                module = None
                # 判断模块是否已存在，如果不存在则添加
                if module_name and apis:
                    module = Module.objects.filter(name=module_name, project_id=project.id, is_deleted=False).first()
                    if not module:
                        # module在系统中不存在
                        if module_name in module_name_list:
                            # Module已经存在于module_list列表中
                            for module_temp in module_list:
                                if module_name == module_temp.name:
                                    module = module_temp
                                    break
                        else:
                            # Module不存在于module_list列表中，新建Module，并将其存放到module_list列表中
                            module = Module(**{
                                'name': module_name,
                                'project': project,
                                'creator': user,
                                'modifier': user
                            })
                            module_name_list.append(module_name)
                            module_list.append(module)
                # 判断接口是否已存在，如果不存在则添加
                for api_temp in apis:
                    api = Api()
                    # api创建人
                    api.creator = user
                    # api修改人
                    api.modifier = user
                    # api所属项目
                    api.project = project
                    # api所属模块
                    api.module = module
                    # api名称
                    api.name = api_temp.get('title')
                    # api请求参数类型
                    request_data_type = api_temp.get('req_body_type', 'json').lower()
                    if request_data_type in ('json', 'raw'):
                        api.request_data_type = 'Json'
                    elif request_data_type == 'form':
                        api.request_data_type = 'Form Data'
                    else:
                        api.request_data_type = 'Json'
                    # api请求方法
                    api.method = api_temp.get('method', 'GET').upper()
                    # api请求头
                    headers = api_temp.get('req_headers')
                    api.headers = json.dumps({header.get('name'): header.get('value', '') for header in headers},
                                             ensure_ascii=False) if headers else json.dumps(
                        {"Content-Type": "application/json"})
                    headers_desc = '## 接口参数定义\n### 请求头参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|'

                    if headers:
                        headers_desc_val = '\n'.join(
                            ['|{name}|||{value}||'.format(name=header.get('name'), value=header.get('value', '')) for
                             header in headers])
                        headers_desc = '\n'.join([headers_desc, headers_desc_val])

                    # api请求URL，api_temp.get('req_params')中的参数为路径参数
                    url = api_temp.get('path', '')
                    api.url = url.replace('{', '{{').replace('}', '}}')

                    # api查询参数
                    req_query = api_temp.get('req_query')
                    request_params = [query.get('name') for query in req_query]
                    api.request_params = '=&'.join(request_params) + '=' if request_params else None
                    # api查询参数定义
                    request_params_desc = '### 查询参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|'
                    if request_params:
                        request_params_desc_val = '\n'.join(
                            ['|{name}|||||'.format(name=request_param) for request_param in request_params])
                        request_params_desc = '\n'.join([request_params_desc, request_params_desc_val])

                    # api请求体参数
                    request_data = {}
                    req_body_other = api_temp.get('req_body_other')
                    if req_body_other:
                        req_body_other = json.loads(req_body_other)
                        properties = req_body_other.get('properties')
                        if properties:
                            request_data = {key: '' for key, value in properties.items()}
                    api.request_data = json.dumps(request_data, ensure_ascii=False)
                    # api请求体参数定义
                    request_data_desc = '### Body参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|'
                    if request_data:
                        request_data_desc_val = '\n'.join(
                            ['|{name}|{type}||||'.format(name=key, type=value) for key, value in
                             request_data.items()])
                        request_data_desc = '\n'.join([request_data_desc, request_data_desc_val])
                    # api备注
                    api.remark = api_temp.get('desc')
                    # api响应参数定义
                    response_data_desc = '### 响应结果参数定义\n|参数名称|类型|是否必须|默认值|描述|\n|-|-|-|-|-|'
                    api.parameter_desc = '\n'.join(
                        [headers_desc, request_params_desc, request_data_desc, response_data_desc])
                    if not Api.objects.filter(project_id=project.id, url=api.url, method=api.method, is_deleted=False):
                        api_list.append(api)
                    else:
                        logger.info('通过postman文件导入的接口已存在，接口：{}'.format(api.url + '\t' + api.method))

    for content in contents:
        content = json.loads(content)
        _parse_api(content=content)
    Module.objects.bulk_create(module_list)
    Api.objects.bulk_create(api_list)

    msg = '通过yapi json文件成功导入{module_total}个模块，{api_total}个接口'.format(module_total=len(module_list),
                                                                     api_total=len(api_list))
    logger.info(msg)
    return msg
